﻿using System;
using System.Windows.Input;

namespace OpenLightGroup.Common.Mvvm
{
    /// <summary>
    /// Class that implements ICommand and allows delegates to be assigned to the Execute and CanExecute methods.
    /// </summary>
    public class DelegateCommand
        : ICommand
    {
        private Func<object, bool> canExecute;
        private Action<object> executeAction;
        private bool canExecuteCache = false;


        /// <summary>
        /// Creates new instance of DelegateCommand, this constructor does not except a delegate for CanExecute.
        /// </summary>
        /// <param name="executeAction">Delegate to be executed by ICommand.Execute</param>
        public DelegateCommand(Action<object> executeAction)
            : this(executeAction, null)
        {

        }

        /// <summary>
        /// Creates new instance of DelegateCommand, this constructor does not except a delegate for CanExecute.
        /// </summary>
        /// <param name="executeAction">Delegate to be executed by ICommand.Execute</param>
        /// <param name="canExecute">Delegate to be executed by ICommand.CanExecute</param>
        public DelegateCommand(Action<object> executeAction, Func<object, bool> canExecute)
        {
            this.canExecute = canExecute;
            this.executeAction = executeAction;
        }

        #region ICommand Members

        /// <summary>
        /// Event to notify listeners that the CanExecute return has changed.
        /// </summary>
        public event EventHandler CanExecuteChanged;

        /// <summary>
        /// Satisfies the ICommand.CanExecute contract by executing the delegate passed into the overloaded constructor. 
        /// If no delegate was provided, it will always return true.
        /// </summary>
        /// <param name="parameter">Object that can be used to determine if the command can be executed.</param>
        /// <returns>Returns true if the command should be allowed to execute.</returns>
        public bool CanExecute(object parameter)
        {

            if (this.canExecute == null) return true;

            var output = this.canExecute(parameter);

            if (output != this.canExecuteCache)
            {
                this.canExecuteCache = output;

                this.CanExecuteChanged.Raise(this, EventArgs.Empty);

            }

            return this.canExecuteCache;

        }


        /// <summary>
        /// Satisfies the ICommand.Execute contract by executing the delegate passed into the constructor. 
        /// </summary>
        /// <param name="parameter">An object that can be used in the delegate method.</param>
        public void Execute(object parameter)
        {
            this.executeAction(parameter);
        }

        #endregion

        /// <summary>
        /// Allows other objects to force the CanExecuteChanged event to raise.
        /// </summary>
        public void RaiseCanExecuteChanged()
        {
            this.CanExecuteChanged.Raise(this, EventArgs.Empty);
        }

    }
}
